package org.gdstash.character;

import org.gdstash.db.*;
import org.gdstash.file.GDFileSize;
import org.gdstash.file.GDReader;
import org.gdstash.file.GDWriter;
import org.gdstash.item.GDAbstractContainer;
import org.gdstash.item.GDItem;
import org.gdstash.ui.character.GDCharFactionPane;
import org.gdstash.ui.character.GDCharMasteryImagePane;
import org.gdstash.util.FileVersionException;
import org.gdstash.util.GDConstants;
import org.gdstash.util.GDMsgFormatter;
import org.gdstash.util.GDMsgLogger;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

public class GDChar implements GDFileSize {
    public static final byte BYTE_VANILLA = 0;
    public static final byte BYTE_ASHES_OF_MALMOUTH = 1;
    public static final byte BYTE_FORGOTTEN_GODS = 3;
    public static final byte BYTE_FANGS_OF_ASTERKARN = 7;
    public static final byte BYTE_FALSE = 0;
    public static final byte BYTE_TRUE = 1;
    public static final byte SEX_FEMALE = 0;
    public static final byte SEX_MALE = 1;
    public static final String TEXTURE_MALE = "creatures/pc/hero02.tex";
    public static final byte SOFTCORE = 0;
    public static final byte HARDCORE = 1;
    public static final byte DIFFICULTY_NORMAL = 0;
    public static final byte DIFFICULTY_ELITE = 1;
    public static final byte DIFFICULTY_ULTIMATE = 2;
    public static final byte CRUCIBLE_ASPIRANT = 0;
    public static final byte CRUCIBLE_CHALLENGER = 1;
    public static final byte CRUCIBLE_GLADIATOR = 2;
    public static final int EQUIP_SLOT_HEAD = 0;
    public static final int EQUIP_SLOT_AMULET = 1;
    public static final int EQUIP_SLOT_CHEST = 2;
    public static final int EQUIP_SLOT_LEGS = 3;
    public static final int EQUIP_SLOT_FEET = 4;
    public static final int EQUIP_SLOT_HANDS = 5;
    public static final int EQUIP_SLOT_RING_RIGHT = 6;
    public static final int EQUIP_SLOT_RING_LEFT = 7;
    public static final int EQUIP_SLOT_BELT = 8;
    public static final int EQUIP_SLOT_SHOULDERS = 9;
    public static final int EQUIP_SLOT_MEDAL = 10;
    public static final int EQUIP_SLOT_RELIC = 11;
    public static final int EQUIP_SLOT_WEAPON_1_RIGHT = 12;
    public static final int EQUIP_SLOT_WEAPON_1_LEFT = 13;
    public static final int EQUIP_SLOT_WEAPON_2_RIGHT = 14;
    public static final int EQUIP_SLOT_WEAPON_2_LEFT = 15;
    public static final int EQUIP_SLOT_HAND_RIGHT = 0;
    public static final int EQUIP_SLOT_HAND_LEFT = 1;
    private static final int VERSION_1 = 1;
    private static final int VERSION_2 = 2;
    private static final int VERSION_6 = 6;
    private static final int VERSION_7 = 7;
    private static final int VERSION_8 = 8;
    private File file;
    private int key;
    private int version;
    private boolean charError;
    private GDCharHeader header;
    private GDCharUID uid;
    private GDCharInfo info;
    private GDCharBio bio;
    private GDCharInventory inventory;
    private GDCharStash stash;
    private GDCharRespawnList respawns;
    private GDCharTeleportList teleports;
    private GDCharMarkerList markers;
    private GDCharShrineList shrines;
    private GDCharSkillList skills;
    private GDCharNoteList notes;
    private GDCharFactionList factions;
    private GDCharUISettings ui;
    private GDCharTutorialList tutorials;
    private GDCharStats stats;
    private GDCharCrucible crucible;

    public GDChar(final File file) {
        this.file = file;
        this.charError = false;
        this.header = new GDCharHeader();
        this.uid = new GDCharUID();
        this.info = new GDCharInfo();
        this.bio = new GDCharBio();
        this.inventory = new GDCharInventory(this);
        this.stash = new GDCharStash(this);
        this.respawns = new GDCharRespawnList();
        this.teleports = new GDCharTeleportList(this);
        this.markers = new GDCharMarkerList();
        this.shrines = new GDCharShrineList(this);
        this.skills = new GDCharSkillList();
        this.notes = new GDCharNoteList();
        this.factions = new GDCharFactionList();
        this.ui = new GDCharUISettings();
        this.tutorials = new GDCharTutorialList();
        this.stats = new GDCharStats();
        this.crucible = new GDCharCrucible();
    }

    public String getCharName() {
        if (this.header == null) {
            return null;
        }
        return this.header.getCharName();
    }

    public byte getSex() {
        if (this.header == null) {
            return 1;
        }
        return this.header.getSex();
    }

    public int getGenderCode() {
        int genderCode = 0;
        final byte sex = this.getSex();
        if (sex == 0) {
            genderCode = 1;
        }
        return genderCode;
    }

    public boolean isHardcore() {
        return this.header != null && this.header.isHardcore();
    }

    public boolean isAsheshOfMalmouthChar() {
        return this.header != null && this.header.isAsheshOfMalmouthChar();
    }

    public boolean isForgottenGodsChar() {
        return this.header != null && this.header.isForgottenGodsChar();
    }

    public static GDCharSummary readCharSummary(final File file) {
        String path = null;
        try {
            path = file.getCanonicalPath();
        } catch (IOException ex) {
            return null;
        }
        final GDChar gdc = new GDChar(file);
        gdc.readSummary(path);
        if (gdc.hasErrors()) {
            return null;
        }
        return new GDCharSummary(gdc.getCharHeader(), gdc.getCharInfo());
    }

    public boolean isInMainQuest() {
        return this.info != null && this.info.isInMainQuest();
    }

    public byte getGreatestCampaignDifficulty() {
        if (this.info == null) {
            return 0;
        }
        return this.info.getGreatestCampaignDifficulty();
    }

    public byte getGreatestCrucibleDifficulty() {
        if (this.info == null) {
            return 0;
        }
        return this.info.getGreatestCrucibleDifficulty();
    }

    public int getMoney() {
        if (this.info == null) {
            return 0;
        }
        return this.info.getMoney();
    }

    public int getTributes() {
        if (this.info == null) {
            return 0;
        }
        return this.info.getTributes();
    }

    public int getLevel() {
        if (this.bio == null) {
            return 1;
        }
        return this.bio.getLevel();
    }

    public int getExperience() {
        if (this.bio == null) {
            return 0;
        }
        return this.bio.getExperience();
    }

    public int getStatPoints() {
        if (this.bio == null) {
            return 0;
        }
        return this.bio.getStatPoints();
    }

    public int getSkillPoints() {
        if (this.bio == null) {
            return 0;
        }
        return this.bio.getSkillPoints();
    }

    public int getDevotionPoints() {
        if (this.bio == null) {
            return 0;
        }
        return this.bio.getDevotionPoints();
    }

    public float getPhysique() {
        if (this.bio == null) {
            return 0.0f;
        }
        return this.bio.getPhysique();
    }

    public float getCunning() {
        if (this.bio == null) {
            return 0.0f;
        }
        return this.bio.getCunning();
    }

    public float getSpirit() {
        if (this.bio == null) {
            return 0.0f;
        }
        return this.bio.getSpirit();
    }

    public float getHealth() {
        if (this.bio == null) {
            return 0.0f;
        }
        return this.bio.getHealth();
    }

    public float getEnergy() {
        if (this.bio == null) {
            return 0.0f;
        }
        return this.bio.getEnergy();
    }

    public int getStashHeight() {
        if (this.stash == null) {
            return 18;
        }
        return this.stash.getHeight();
    }

    public int getStashWidth() {
        if (this.stash == null) {
            return 10;
        }
        return this.stash.getWidth();
    }

    public static void readUIDList(final List<GDCharUID> uidList) throws IOException {
        final int len = GDReader.readEncInt(true);
        uidList.clear();
        for (int i = 0; i < len; ++i) {
            final GDCharUID uid = new GDCharUID();
            uid.read();
            uidList.add(uid);
        }
    }

    public GDCharUID getRespawnPoint(final int difficulty) {
        if (this.respawns == null) {
            return null;
        }
        return this.respawns.getRespawnPoint(difficulty);
    }

    public List<GDCharUID> getRiftList(final int difficulty) {
        final List<GDCharUID> list = new LinkedList<GDCharUID>();
        if (this.teleports == null) {
            return list;
        }
        return this.teleports.getRiftList(difficulty);
    }

    public List<GDCharUID> getDiscoveredShrinesList(final int difficulty) {
        final List<GDCharUID> list = new LinkedList<GDCharUID>();
        if (this.shrines == null) {
            return list;
        }
        return this.shrines.getDiscoveredShrinesList(difficulty);
    }

    public List<GDCharUID> getRestoredShrinesList(final int difficulty) {
        final List<GDCharUID> list = new LinkedList<GDCharUID>();
        if (this.shrines == null) {
            return list;
        }
        return this.shrines.getRestoredShrinesList(difficulty);
    }

    public int getSkillReclaimPoints() {
        if (this.skills == null) {
            return 0;
        }
        return this.skills.getSkillReclaimPoints();
    }

    public int getDevotionReclaimPoints() {
        if (this.skills == null) {
            return 0;
        }
        return this.skills.getDevotionReclaimPoints();
    }

    public int getSkillMaxLevel(final String skillID) {
        if (this.skills == null) {
            return 0;
        }
        return this.skills.getSkillMaxLevel(skillID);
    }

    public int[] getFactionReputations() {
        int[] reputations = null;
        if (this.factions == null) {
            reputations = new int[GDCharFactionPane.FACTIONS.length];
            for (int i = 0; i < reputations.length; ++i) {
                reputations[i] = 0;
            }
        } else {
            reputations = this.factions.getFactionReputations();
        }
        return reputations;
    }

    public int getDeaths() {
        if (this.stats == null) {
            return 0;
        }
        return this.stats.getDeaths();
    }

    public String getPlayTimeAsDHM() {
        if (this.stats == null) {
            return null;
        }
        return this.stats.getPlayTimeAsDHM();
    }

    public static void writeUIDList(final List<GDCharUID> uidList) throws IOException {
        int len = 0;
        if (uidList != null) {
            len = uidList.size();
        }
        GDWriter.writeInt(len);
        if (uidList == null) {
            return;
        }
        for (final GDCharUID uid : uidList) {
            uid.write();
        }
    }

    public boolean isFangsOfAsterkarnChar() {
        return this.header != null && this.header.isFangsOfAsterkarnChar();
    }

    public int getKillsMonster() {
        if (this.stats == null) {
            return 0;
        }
        return this.stats.getKillsMonster();
    }

    public int getKillsHero() {
        if (this.stats == null) {
            return 0;
        }
        return this.stats.getKillsHero();
    }

    public int getKillsNemesis() {
        if (this.stats == null) {
            return 0;
        }
        return this.stats.getKillsNemesis();
    }

    public int getLoreNotesCollected() {
        if (this.stats == null) {
            return 0;
        }
        return this.stats.getLoreNotesCollected();
    }

    public int getOneShotChestsOpened() {
        if (this.stats == null) {
            return 0;
        }
        return this.stats.getOneShotChestsOpened();
    }

    public int getItemsCrafted() {
        if (this.stats == null) {
            return 0;
        }
        return this.stats.getItemsCrafted();
    }

    public int getRelicsCrafted() {
        if (this.stats == null) {
            return 0;
        }
        return this.stats.getRelicsCrafted();
    }

    public int getRelicsTranscendentCrafted() {
        if (this.stats == null) {
            return 0;
        }
        return this.stats.getRelicsTranscendentCrafted();
    }

    public int getRelicsMythicalCrafted() {
        if (this.stats == null) {
            return 0;
        }
        return this.stats.getRelicsMythicalCrafted();
    }

    public boolean hasErrors() {
        return this.charError;
    }

    public boolean hasChanged() {
        boolean hasChanged = false;
        hasChanged = (hasChanged || this.header.hasChanged());
        hasChanged = (hasChanged || this.info.hasChanged());
        hasChanged = (hasChanged || this.bio.hasChanged());
        hasChanged = (hasChanged || this.inventory.hasChanged());
        hasChanged = (hasChanged || this.stash.hasChanged());
        hasChanged = (hasChanged || this.respawns.hasChanged());
        hasChanged = (hasChanged || this.teleports.hasChanged());
        hasChanged = (hasChanged || this.shrines.hasChanged());
        hasChanged = (hasChanged || this.skills.hasChanged());
        hasChanged = (hasChanged || this.factions.hasChanged());
        hasChanged = (hasChanged || this.stats.hasChanged());
        return hasChanged;
    }

    public GDCharEquippedContainer getEquipment() {
        GDCharEquippedContainer equipment = null;
        if (this.inventory != null) {
            equipment = this.inventory.getEquipment();
        }
        return equipment;
    }

    public GDCharInventorySack getInventory() {
        GDCharInventorySack sack = null;
        if (this.inventory != null) {
            sack = this.inventory.getInventory();
        }
        return sack;
    }

    public List<GDCharInventorySack> getBags() {
        List<GDCharInventorySack> list = new LinkedList<GDCharInventorySack>();
        if (this.inventory != null) {
            list = this.inventory.getBags();
        }
        return list;
    }

    public GDCharStash getStash() {
        return this.stash;
    }

    public List<GDItem> getItems() {
        final List<GDItem> list = new LinkedList<GDItem>();
        list.addAll(this.inventory.getItems());
        list.addAll(this.stash.getItemList());
        return list;
    }

    private List<GDCharSkill> getMasteries() {
        List<GDCharSkill> masteries = null;
        if (this.skills == null) {
            masteries = new LinkedList<GDCharSkill>();
        } else {
            masteries = this.skills.getMasteries();
        }
        return masteries;
    }

    public SkillInfo[] getMasteryInfo() {
        final List<GDCharSkill> masteries = this.getMasteries();
        if (masteries.size() == 0) {
            return null;
        }
        final SkillInfo[] infos = new SkillInfo[masteries.size()];
        int i = 0;
        for (final GDCharSkill skill : masteries) {
            final SkillInfo info = new SkillInfo(skill, this);
            infos[i] = info;
            ++i;
        }
        return infos;
    }

    public List<GDCharSkill> getCharSkillList() {
        List<GDCharSkill> list = null;
        if (this.skills == null) {
            list = new LinkedList<GDCharSkill>();
        } else {
            list = this.skills.getCharSkillList();
        }
        return list;
    }

    public List<GDCharSkill> getSkillsByMastery(final String masteryID) {
        List<GDCharSkill> list = null;
        if (this.skills == null) {
            list = new LinkedList<GDCharSkill>();
        } else {
            list = this.skills.getSkillsByMastery(masteryID);
        }
        return list;
    }

    private List<GDCharSkill> getDevotions() {
        List<GDCharSkill> devotions = null;
        if (this.skills == null) {
            devotions = new LinkedList<GDCharSkill>();
        } else {
            devotions = this.skills.getDevotions();
        }
        return devotions;
    }

    public SkillInfo[] getDevotionInfo() {
        final List<GDCharSkill> devotions = this.getDevotions();
        if (devotions.size() == 0) {
            return null;
        }
        final SkillInfo[] infos = new SkillInfo[devotions.size()];
        int i = 0;
        for (final GDCharSkill skill : devotions) {
            final SkillInfo info = new SkillInfo(skill);
            infos[i] = info;
            ++i;
        }
        return infos;
    }

    private GDCharHeader getCharHeader() {
        return this.header;
    }

    private GDCharInfo getCharInfo() {
        return this.info;
    }

    public boolean isRoTCampaign() {
        boolean rot = false;
        for (final GDCharUID uid : this.teleports.getRiftList(0)) {
            if (uid.equals(GDCharTeleportList.UID_RIFT_ROT_01_ROGUE_ENCAMPMENT)) {
                rot = true;
                break;
            }
        }
        return rot;
    }

    public void setFileDir(final File fDir) {
        try {
            final String name = this.file.getName();
            String dir = fDir.getCanonicalPath();
            dir = dir + GDConstants.FILE_SEPARATOR + name;
            final File f = new File(dir);
            this.file = f;
        } catch (IOException ex) {
            GDMsgLogger.addError(ex);
        }
    }

    public List<GDAbstractContainer> getStashPages() {
        if (this.stash == null) {
            final List<GDAbstractContainer> list = new LinkedList<GDAbstractContainer>();
            return list;
        }
        return this.stash.getStashPages();
    }

    public int getGreatestDamageInflicted() {
        if (this.stats == null) {
            return 0;
        }
        return this.stats.getGreatestDamageInflicted();
    }

    public void setGreatestDamageInflicted(final float damage) {
        this.stats.setGreatestDamageInflicted(damage);
    }

    public int getGreatestDamageReceived() {
        if (this.stats == null) {
            return 0;
        }
        return this.stats.getGreatestDamageReceived();
    }

    public void setGreatestDamageReceived(final float damage) {
        this.stats.setGreatestDamageInflicted(damage);
    }

    public void setCharName(final String charName) {
        this.header.setCharName(charName);
    }

    public void setSex(final byte sex) {
        this.header.setSex(sex);
        if (sex == 1) {
            this.info.setTexture("creatures/pc/hero02.tex");
        } else {
            this.info.setTexture(null);
        }
    }

    public void setHardcore(final boolean hardcore) {
        this.header.setHardcore(hardcore);
    }

    public void setInMainQuest(final boolean isInMainQuest) {
        this.info.setInMainQuest(isInMainQuest);
    }

    public void setGreatestCampaignDifficulty(final byte greatestDifficulty) {
        this.info.setGreatestCampaignDifficulty(greatestDifficulty);
    }

    public void setGreatestCrucibleDifficulty(final byte greatestDifficulty) {
        this.info.setGreatestCrucibleDifficulty(greatestDifficulty);
    }

    public void setMoney(final int money) {
        this.info.setMoney(money);
    }

    public void setTributes(final int tributes) {
        this.info.setTributes(tributes);
    }

    public void setLevel(final int level) {
        this.header.setLevel(level);
        this.bio.setLevel(level);
        this.stats.setMaxLevel(level);
        int maxMasteries = 0;
        if (level > 1) {
            maxMasteries = 1;
        }
        if (level > 9) {
            maxMasteries = 2;
        }
        this.skills.setMasteriesAllowed(maxMasteries);
    }

    public void setExperience(final int experience) {
        this.bio.setExperience(experience);
    }

    public void setStatPoints(final int modifierPoints) {
        this.bio.setStatPoints(modifierPoints);
    }

    public void setSkillPoints(final int skillPoints) {
        this.bio.setSkillPoints(skillPoints);
    }

    public void setDevotionPoints(final int devotionPoints) {
        this.bio.setDevotionPoints(devotionPoints);
    }

    public void setPhysique(final float physique) {
        this.bio.setPhysique(physique);
    }

    public void setCunning(final float cunning) {
        this.bio.setCunning(cunning);
    }

    public void setRespawnPoint(final int difficulty, final GDCharUID spawn) {
        this.respawns.setRespawnPoint(difficulty, spawn);
    }

    public void setRiftList(final int difficulty, final List<GDCharUID> addList, final List<GDCharUID> removeList) {
        this.teleports.setRiftList(difficulty, addList, removeList);
    }

    public void setDiscoveredShrinesList(final int difficulty, final List<GDCharUID> addList, final List<GDCharUID> removeList) {
        this.shrines.setDiscoveredShrinesList(difficulty, addList, removeList);
    }

    public void setRestoredShrinesList(final int difficulty, final List<GDCharUID> addList, final List<GDCharUID> removeList) {
        this.shrines.setRestoredShrinesList(difficulty, addList, removeList);
        int shrinesRestored = 0;
        shrinesRestored += this.shrines.getDiscoveredShrinesList(0).size();
        shrinesRestored += this.shrines.getDiscoveredShrinesList(1).size();
        shrinesRestored += this.shrines.getDiscoveredShrinesList(2).size();
        this.stats.setShrinesRestored(shrinesRestored);
    }

    public void setSpirit(final int spirit) {
        this.bio.setSpirit((float) spirit);
    }

    public void setHealth(final float health) {
        this.bio.setHealth(health);
    }

    public void setMasterySkills(final String masteryID, final List<GDCharMasteryImagePane.Skill> list) {
        this.skills.setMasterySkills(masteryID, list);
    }

    public void setEnergy(final float energy) {
        this.bio.setEnergy(energy);
    }

    public void setSkillReclaimPoints(final int skillReclamationPointsUsed) {
        this.skills.setSkillReclaimPoints(skillReclamationPointsUsed);
    }

    public void setSkillLevel(final SkillInfo[] infos) {
        this.skills.setSkillLevel(infos);
    }

    public int refundMastery(final String masteryID) {
        if (masteryID == null) {
            return 0;
        }
        final int points = this.skills.refundMastery(masteryID);
        return points;
    }

    public void setDevotionReclaimPoints(final int devotionReclamationPointsUsed) {
        this.skills.setDevotionReclaimPoints(devotionReclamationPointsUsed);
    }

    public void setFactionReputations(final int[] reputations) {
        this.factions.setFactionReputations(reputations);
    }

    public void setDeaths(final int deaths) {
        this.stats.setDeaths(deaths);
        if (deaths == 0) {
            this.tutorials.removeTutorial(23);
        }
    }

    private void readSummary(final String path) {
        int val = 0;
        try {
            GDReader.readEncFileToBuffer(this.file);
            this.key = GDReader.readKey();
            val = GDReader.readEncInt(true);
            if (val != 1480803399) {
                throw new FileVersionException(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_UNSUPPORTED_VERSION"));
            }
            this.header.read();
            val = GDReader.readEncInt(false);
            if (val != 0) {
                throw new FileVersionException(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_UNSUPPORTED_VERSION"));
            }
            this.version = GDReader.readEncInt(true);
            if (this.version != 6 && this.version != 7 && this.version != 8) {
                throw new FileVersionException(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_UNSUPPORTED_VERSION"));
            }
            this.uid.read();
            this.info.read();
        } catch (FileNotFoundException ex) {
            if (path != null) {
                final Object[] args = {path};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_FILE_NOT_FOUND", args);
                GDMsgLogger.addError(msg);
            }
            GDMsgLogger.addError(ex);
            this.charError = true;
        } catch (FileVersionException ex2) {
            if (path != null) {
                final Object[] args = {path};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_FILE_READ", args);
                GDMsgLogger.addError(msg);
            }
            GDMsgLogger.addInfo(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "INFO_UPDATE_GDSTASH"));
            GDMsgLogger.addError(ex2);
            this.charError = true;
        } catch (Exception ex3) {
            if (path != null) {
                final Object[] args = {path};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_FILE_READ", args);
                GDMsgLogger.addError(msg);
            }
            GDMsgLogger.addError(ex3);
            this.charError = true;
        }
    }

    private void listSkills() {
        for (final GDCharSkill sk : this.skills.getCharSkillList()) {
            String s = sk.getSkillName() + " : " + Integer.toString(sk.getSkillLevel());
            if (sk.isEnabled()) {
                s += " [enabled]";
            } else {
                s += " [not enabled]";
            }
            System.out.println(s);
        }
    }

    public void read() {
        String path = null;
        final int val = 0;
        try {
            path = this.file.getCanonicalPath();
            this.readSummary(path);
            if (this.charError) {
                return;
            }
            this.bio.read();
            this.inventory.read();
            this.stash.read();
            this.respawns.read();
            this.teleports.read();
            this.markers.read();
            this.shrines.read();
            this.skills.read();
            this.notes.read();
            this.factions.read();
            this.ui.read();
            this.tutorials.read();
            this.stats.read();
            if (this.version >= 7) {
                this.crucible.read();
            }
        } catch (FileNotFoundException ex) {
            if (path != null) {
                final Object[] args = {path};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_FILE_NOT_FOUND", args);
                GDMsgLogger.addError(msg);
            }
            GDMsgLogger.addError(ex);
            this.charError = true;
        } catch (FileVersionException ex2) {
            if (path != null) {
                final Object[] args = {path};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_FILE_READ", args);
                GDMsgLogger.addError(msg);
            }
            GDMsgLogger.addInfo(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "INFO_UPDATE_GDSTASH"));
            GDMsgLogger.addError(ex2);
            this.charError = true;
        } catch (Exception ex3) {
            if (path != null) {
                final Object[] args = {path};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_FILE_READ", args);
                GDMsgLogger.addError(msg);
            }
            GDMsgLogger.addError(ex3);
            this.charError = true;
        }
    }

    @Override
    public int getByteSize() {
        int size = 0;
        size += 4;
        size += 4;
        size += this.header.getByteSize();
        size += 4;
        size += 4;
        size += GDCharUID.getByteSize();
        size += this.info.getByteSize();
        size += this.bio.getByteSize();
        size += this.inventory.getByteSize();
        size += this.stash.getByteSize();
        size += this.respawns.getByteSize();
        size += this.teleports.getByteSize();
        size += this.markers.getByteSize();
        size += this.shrines.getByteSize();
        size += this.skills.getByteSize();
        size += this.notes.getByteSize();
        size += this.factions.getByteSize();
        size += this.ui.getByteSize();
        size += this.tutorials.getByteSize();
        size += this.stats.getByteSize();
        if (this.version >= 7) {
            size += this.crucible.getByteSize();
        }
        return size;
    }

    public void createBuffer() throws IOException {
        GDWriter.reserveBuffer(this);
        GDWriter.writeInt(1431655765);
        GDWriter.writeInt(1480803399);
        this.header.write();
        GDWriter.writeInt(0);
        GDWriter.writeInt(this.version);
        this.uid.write();
        this.info.write();
        this.bio.write();
        this.inventory.write();
        this.stash.write();
        this.respawns.write();
        this.teleports.write();
        this.markers.write();
        this.shrines.write();
        this.skills.write();
        this.notes.write();
        this.factions.write();
        this.ui.write();
        this.tutorials.write();
        this.stats.write();
        if (this.version >= 7) {
            this.crucible.write();
        }
    }

    public void write() throws IOException {
        this.createBuffer();
        final String path = this.file.getParent() + GDConstants.FILE_SEPARATOR;
        final String tname = "player.g";
        final File temp = new File(path + "temp_tmp.tmp");
        temp.createNewFile();
        GDWriter.writeBuffer(temp);
        File fCurr = new File(path + tname + String.format("%02d", 0));
        File fNew = null;
        for (int i = 9; i >= 0; --i) {
            fCurr = new File(path + tname + String.format("%02d", i));
            fNew = new File(path + tname + String.format("%02d", i + 1));
            if (fCurr.exists()) {
                if (i == 9) {
                    fCurr.delete();
                } else {
                    fCurr.renameTo(fNew);
                }
            }
        }
        this.file.renameTo(fCurr);
        temp.renameTo(this.file);
    }

    public void write2() throws IOException {
        final String path = this.file.getParent() + GDConstants.FILE_SEPARATOR;
        final String tname = "player.g";
        File fCurr = new File(path + tname + String.format("%02d", 0));
        File fNew = null;
        for (int i = 9; i >= 0; --i) {
            fCurr = new File(path + tname + String.format("%02d", i));
            fNew = new File(path + tname + String.format("%02d", i + 1));
            if (fCurr.exists()) {
                if (i == 9) {
                    fCurr.delete();
                } else {
                    fCurr.renameTo(fNew);
                }
            }
        }
        GDWriter.copyFile(this.file, fCurr);
        try {
            this.createBuffer();
            GDWriter.writeBuffer(this.file);
        } catch (IOException ex) {
            GDWriter.copyFile(fCurr, this.file);
            throw ex;
        }
    }

    public static class SkillInfo {
        public String id;
        public String name;
        public int points;

        public SkillInfo(final GDCharSkill skill) {
            if (skill == null) {
                return;
            }
            this.id = skill.getID();
            final DBSkill dbSkill = DBSkill.get(this.id);
            if (dbSkill == null) {
                return;
            }
            this.name = dbSkill.getName();
            this.points = skill.getLevel();
        }

        public SkillInfo(final GDCharSkill skill, final GDChar gdc) {
            if (skill == null) {
                return;
            }
            this.id = skill.getID();
            final DBSkill dbSkill = DBSkill.get(this.id);
            if (dbSkill == null) {
                return;
            }
            this.name = dbSkill.getName();
            final String treeID = DBSkillTreeAlloc.getTreeIDBySkillID(this.id);
            if (treeID != null) {
                final DBEnginePlayer player = DBEnginePlayer.get();
                final DBEnginePlayerMasteries mt = player.retrieveID(treeID);
                if (mt != null) {
                    final int index = mt.getIndex();
                    final String tag = "tagSkillClassName" + String.format("%02d", index);
                    final DBEngineTagText text = DBEngineTagText.get(tag);
                    if (text != null) {
                        this.name = text.getText(gdc.getGenderCode());
                    }
                }
            }
            this.points = skill.getLevel();
        }

        public SkillInfo(final String skillID, final int level) {
            if (skillID == null) {
                return;
            }
            this.id = skillID;
            final DBSkill dbSkill = DBSkill.get(this.id);
            this.name = dbSkill.getName();
            this.points = level;
        }
    }
}
